import backtrader as bt
from datetime import datetime
import pandas as pd
import akshare as ak
import time
import requests

# -- 全局超时设置 --
# 为akshare底层的requests请求设置一个15秒的超时
requests.adapters.DEFAULT_TIMEOUT = 15

# -- 股票信息缓存 --
STOCK_INFO_CACHE = {}
CACHE_TTL_SECONDS = 24 * 60 * 60  # 24小时

# 动态导入策略
from app.strategies.ma_cross_strategy import MaCrossStrategy
from app.strategies.rsi_strategy import RsiStrategy
from app.strategies.buy_and_hold_strategy import BuyAndHoldStrategy
from app.strategies.bollinger_bands_strategy import BollingerBandsStrategy
from app.strategies.macd_strategy import MacdStrategy
from app.strategies.mean_reversion_strategy import MeanReversionStrategy
from app.strategies.momentum_strategy import MomentumStrategy
from app.strategies.breakout_strategy import BreakoutStrategy
from app.strategies.volatility_strategy import VolatilityStrategy
from app.data.data_provider import get_stock_data, get_financial_data
from app.engine.commission_scheme import AStockCommission

# 策略注册表
STRATEGY_MAP = {
    "ma_cross": MaCrossStrategy,
    "rsi": RsiStrategy,
    "buy_and_hold": BuyAndHoldStrategy,
    "bollinger": BollingerBandsStrategy,
    "macd": MacdStrategy,
    "mean_reversion": MeanReversionStrategy,
    "momentum": MomentumStrategy,
    "breakout": BreakoutStrategy,
    "volatility": VolatilityStrategy,
}

def run_backtest(
    code: str, 
    start_date: str, 
    end_date: str, 
    strategy_name: str, 
    initial_cash: float = 100000.0,
    position_sizer: int = 95,
    commission_rate: float = 0.0003,
):
    """
    运行回测的核心函数
    """
    print(f"=== 开始回测 ===")
    print(f"股票代码: {code}")
    print(f"回测周期: {start_date} 至 {end_date}")
    print(f"策略名称: {strategy_name}")
    print(f"初始资金: {initial_cash}")
    print(f"仓位比例: {position_sizer}%")
    print(f"佣金费率: {commission_rate}")
    
    # 1. 初始化Cerebro引擎
    print("1. 初始化Cerebro引擎...")
    cerebro = bt.Cerebro()

    # 2. 获取股票信息并自动识别品种 (带缓存和超时)
    print("2. 获取股票信息...")
    stock_name = "未知"
    asset_type = 'stock'
    
    current_time = time.time()
    cache_valid = False
    if code in STOCK_INFO_CACHE:
        cached_data, fetch_time = STOCK_INFO_CACHE[code]
        if current_time - fetch_time < CACHE_TTL_SECONDS:
            stock_name, asset_type = cached_data
            cache_valid = True
            print(f"✓ 从缓存加载股票 {code} 的信息: {stock_name}")

    if not cache_valid:
        print(f"⚠ 通过网络获取股票 {code} 的信息 (超时: {requests.adapters.DEFAULT_TIMEOUT}s)...")
        try:
            info_df = ak.stock_individual_info_em(symbol=code)
            fetched_name = info_df.loc[info_df['item'] == '股票简称', 'value'].iloc[0]
            
            stock_name = fetched_name
            if 'ETF' in stock_name.upper():
                asset_type = 'etf'
            
            # 存入缓存
            STOCK_INFO_CACHE[code] = ((stock_name, asset_type), current_time)
            print(f"✓ 已缓存股票 {code} 的信息: {stock_name}")
            
        except Exception as e:
            print(f"✗ 获取股票 {code} 的详细信息失败 (可能超时或网络错误): {e}")
            # 如果请求失败，我们仍然可以继续回测，只是股票名称显示为"未知"

    # 3. 获取并准备数据
    print("3. 获取股票数据...")
    df = get_stock_data(code, start_date, end_date)
    if df.empty:
        error_msg = f"无法获取 {code} 的数据"
        print(f"✗ {error_msg}")
        return {"error": error_msg}
    
    print(f"✓ 成功获取 {len(df)} 条股票数据")
    
    # 将DataFrame列名转换为backtrader期望的格式
    print("4. 转换数据格式...")
    df['date'] = pd.to_datetime(df['日期'])
    df.set_index('date', inplace=True)
    df['open'] = df['开盘']
    df['high'] = df['最高']
    df['low'] = df['最低']
    df['close'] = df['收盘']
    df['volume'] = df['成交量']

    # 创建Backtrader数据源
    data = bt.feeds.PandasData(
        dataname=df,
        fromdate=datetime.strptime(start_date, '%Y-%m-%d'),
        todate=datetime.strptime(end_date, '%Y-%m-%d')
    )
    cerebro.adddata(data)

    # 4. 智能添加策略和参数
    print("5. 添加策略...")
    if strategy_name not in STRATEGY_MAP:
        error_msg = f"策略 '{strategy_name}' 不存在"
        print(f"✗ {error_msg}")
        return {"error": error_msg}
    
    strategy_class = STRATEGY_MAP[strategy_name]
    strategy_params = {}

    # 只为需要财务数据的策略获取并传递数据
    if strategy_name == 'macd':
        print("  - 获取财务数据用于MACD策略...")
        financial_df = get_financial_data(code)
        strategy_params['financial_data'] = financial_df
    
    cerebro.addstrategy(strategy_class, **strategy_params)
    print(f"✓ 已添加策略: {strategy_class.__name__}")

    # 5. 设置初始资金、仓位管理器和交易成本
    print("6. 设置交易参数...")
    cerebro.broker.setcash(initial_cash)
    
    # 添加仓位管理器
    cerebro.addsizer(bt.sizers.PercentSizer, percents=position_sizer)

    # 添加交易成本方案
    is_stock = (asset_type == 'stock')
    comm_scheme = AStockCommission(
        commission=commission_rate, 
        stocklike=is_stock
    )
    cerebro.broker.addcommissioninfo(comm_scheme)
    print(f"✓ 已设置初始资金: {initial_cash}")
    print(f"✓ 已设置仓位比例: {position_sizer}%")
    print(f"✓ 已设置佣金费率: {commission_rate}")

    # 6. 添加分析器 (使用更兼容的名称)
    print("7. 添加分析器...")
    cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharperatio')
    cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')
    cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
    cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='tradeanalyzer')
    print("✓ 已添加所有分析器")

    # 7. 运行回测
    print("8. 开始运行回测...")
    start_time = time.time()
    results = cerebro.run()
    end_time = time.time()
    strat = results[0]
    print(f"✓ 回测完成，耗时: {end_time - start_time:.2f}秒")

    # 8. 提取并返回结果
    print("9. 提取分析结果...")
    final_value = cerebro.broker.getvalue()
    pnl = final_value - initial_cash
    
    analysis = strat.analyzers.tradeanalyzer.get_analysis()
    sharpe_analysis = strat.analyzers.sharperatio.get_analysis()
    drawdown_analysis = strat.analyzers.drawdown.get_analysis()

    # 在返回结果中加入新的参数
    result = {
        "策略名称": STRATEGY_MAP[strategy_name].__name__,
        "股票代码": code,
        "股票名称": stock_name,
        "交易品种": asset_type,
        "仓位比例(%)": position_sizer,
        "佣金费率(%)": commission_rate * 100,
        "回测周期": f"{start_date} 至 {end_date}",
        "初始资金": initial_cash,
        "期末总资产": final_value,
        "总盈亏": pnl,
        "收益率(%)": (pnl / initial_cash) * 100,
        "夏普比率": sharpe_analysis.get('sharperatio') if sharpe_analysis else 0,
        "最大回撤(%)": drawdown_analysis.max.drawdown,
        "最大回撤金额": drawdown_analysis.max.moneydown,
        "总交易次数": analysis.get('total', {}).get('total', 0),
        "盈利次数": analysis.get('won', {}).get('total', 0),
        "亏损次数": analysis.get('lost', {}).get('total', 0),
        "胜率(%)": (analysis.get('won', {}).get('total', 0) / analysis.get('total', {}).get('total', 0)) * 100 if analysis.get('total', {}).get('total', 0) > 0 else 0,
    }
    
    print("=== 回测完成 ===")
    print(f"最终资产: {final_value:.2f}")
    print(f"总盈亏: {pnl:.2f}")
    print(f"收益率: {(pnl / initial_cash) * 100:.2f}%")
    
    return result 